package com.cskaoyan.javase.oop2.encapsulate._1extends._7initialization._1basic;

/**
 * 我们通过一个引例，练习并讲解这个问题：
 *      创建一个Person类，有name属性和eat方法。
 *      创建一个Star类，有stageName属性和sing方法。
 *      Star类要继承Person类。
 * 创建Star这个子类对象，通过画图/运行代码等研究子类对象初始化的过程。
 *
 * 创建子类Star对象,思考以下问题:
 *      1.new对象是一个类加载的时机
 *          new创建子类对象,肯定会触发子类的类加载,那么会不会触发父类的类加载呢?
 *          肯定会,因为子类要继承父类的成员,JVM需要知道继承了什么东西.
 *          而且类加载的顺序是"先父后子"的
 *
 *         总结:
 *              触发子类的类加载,会优先类加载父类,这也是一种类加载的时机
 *              类加载的时机:
 *                  a.启动main方法
 *                  b.new对象
 *                  c.访问静态成员
 *                  d.触发子类类加载,会优先类加载父类
 *
 *      2.子类对象可以访问父类成员,有没有可能创建了两个对象呢?父类+子类对象?
 *          创建子类对象,创建了几个对象?
 *          只会创建一个对象,那就是子类对象,不会存在任何父类对象
 *
 *      3.子类对象的内存是如何划分的呢?
 *          因为子类对象中要存自身成员变量,也需要存父类成员变量
 *          所以:
 *          一个子类对象当中是要划分区域进行成员变量存储的
 *          子类对象中会专门开辟一片独立的区域，用来存储父类的成员变量，而子类自身的成员仍会存储在自身对象当中。
 *          一个子类对象就要划分为两个区域:
 *              a.子类自身成员变量的区域,就是子类对象自身,用this关键字(引用)指向它
 *              b.父类成员变量的区域,可以近似的看成是一个父类对象,用super关键字指向这片区域
 *
 *      4.接下来探讨子类对象中父子类成员变量的赋值
 *          首先默认初始化永远最先执行,这不是程序员能够控制的
 *          在程序员能够控制的所有赋值手段执行之前,默认初始化就已经执行了
 *          所以这里探究赋值,排除默认初始化
 *          排除默认初始化后,还剩下:
 *              a.显式赋值
 *              b.构造代码块赋值
 *              c.构造器赋值
 *          这三种赋值方式,父类中有,子类中肯定也有
 *          在各自类的区域内部,它们的执行顺序,我们已经学习过了.
 *          现在有了继承,有了父子类
 *          那么父子类的赋值顺序应该如何执行?
 *          先进行父类的赋值,还是进行子类?
 *          理论上来说,也应该是先父后子的,实际上也确实是这样的
 *
 *      5.父子类成员变量在子类对象中赋值,是遵循"先父后子"原则的
 *
 *      6.(重点)为什么赋值顺序是"先父后子"的,如何来保证的?
 *      只需要保证父类内部的赋值方式,最后执行的,要先于子类内部赋值方式,最先执行的
 *      就可以了
 *      只需要保证父类的构造器,先于子类构造器执行就可以了
 *      实际上也就是这样的
 *
 *      7.(重点)为什么父类构造器会先于子类构造器执行呢?
 *      为什么父类构造器会执行?
 *      new子类对象,和父类的构造器有什么关系?
 *
 *      两种子类对象初始化的方式,两种保证父类构造器先于子类构造器执行的方式:
 *      a.子类对象的隐式初始化
 *          (重点)在一个类的构造器中,如果该构造器的第一行既没有用this,也没有用super表示调用其他构造器
 *              那么就隐含 "super();"
 *              表示调用父类的无参构造.
 *
 *      b.子类对象的显式初始化
 *          隐式初始化会调用父类的无参构造器
 *          这样就不能给父类成员变量赋值了
 *          如果想要赋值,就需要显式的调用父类的有参构造器
 *          这就是子类对象的显式初始化
 *          语法:
 *          super(实参列表);
 *          和this表示调用构造器语法完全一样
 *          区别在于
 *          this表示调用自身构造器
 *          super表示调用父类构造器
 *
 *
 * @since 10:51
 * @author wuguidong@cskaoyan.onaliyun.com
 */
public class Demo {
    public static void main(String[] args) {
        Star s = new Star();
        // System.out.println(s.stageName);

        Star s2 = new Star("李四");
        System.out.println(s2.name);
        System.out.println(s2.stageName);
    }
}

class Person {

    /*static {
        System.out.println("Person类进行类加载了!");
    }*/

    {
        System.out.println("Person类的构造器执行了!");
    }
    String name = "张三";

    public void eat() {
        System.out.println("人都要吃饭!");
    }

    public Person() {
    }

    public Person(String name) {
        this.name = name;
    }
}

class Star extends Person {

    /*static {
        System.out.println("Star类进行类加载了!");
    }*/

    {
        System.out.println("Star类的构造器执行了!");
    }
    String stageName = name + "的弟弟";

    public void sing() {
        System.out.println("只因你太美!");
    }

    public Star() {
        // this();
        /*
            在这里有一种语法可以调用父类构造器,那么就能够保证父类构造器先执行了
            这种语法就是super(); 表示调用直接父类的某个构造器
         */
        // super();
    }

    public Star(String name){
        super(name);
    }
}